#pragma once

#include "muduo/net/TcpClient.h"
#include "muduo/net/EventLoopThread.h"
#include "muduo/base/CountDownLatch.h"
#include "muduo/net/TcpConnection.h"

#include "proto/dispatcher.h"
#include "proto/codec.h"

#include "../proto_dir/log_proto.pb.h"

#include "../log_service/message.hpp"

#include <unordered_map>
#include <mutex>
#include <condition_variable>

namespace ns_log
{
    using namespace ns_log_proto;

    using commonResponsePtr = std::shared_ptr<commonResponse>;

    using namespace ns_helper;

    class LogClient
    {
    public:
        using ptr = std::shared_ptr<LogClient>;

        LogClient(const std::string &server_ip, uint16_t server_port)
            : _client(_loop_thread.startLoop(), muduo::net::InetAddress(server_ip, server_port), "LogClient"),
              _dispatcher(std::bind(&LogClient::OnUnknownCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)),
              _codec(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)),
              _latch(1)
        {
            _dispatcher.registerMessageCallback<commonResponse>(std::bind(&LogClient::OnCommonResponse, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _client.setConnectionCallback(std::bind(&LogClient::OnConnection, this, std::placeholders::_1));
            _client.setMessageCallback(std::bind(&ProtobufCodec::onMessage, &_codec, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            start();
        }

        bool connect(const std::string &connection_id)
        {
            openConnectionRequest req;
            req.set_rid(UuidHelper::uuid());
            req.set_connection_id(connection_id);
            if (!_conn->connected())
            {
                std::cout << "连接已断开, 无法创建Connection\n";
                return false;
            }
            // 发送req
            _codec.send(_conn, req);
            commonResponsePtr resp = waitResponse(req.rid());
            std::cout << "创建连接情况: " << resp->err_msg() << "\n";
            return resp->ok();
        }

        bool declareLogger(const std::string &logger_name, LoggerType logger_type, LogLevel::value level, const std::string &pattern, AsyncType async_type, const std::vector<std::vector<std::string>> &sink_vec)
        {
            // 1. 构建req
            declareLoggerRequest req;
            req.set_rid(UuidHelper::uuid());
            req.set_logger_name(logger_name);
            req.set_logger_type(getloggerType(logger_type));
            req.set_limit_level(get_logLevel(level));
            req.set_format_pattern(pattern);
            req.set_async_type(get_asyncType(async_type));

            for (int i = 0; i < sink_vec.size(); i++)
            {
                auto sinks = sink_vec[i];
                sink_args *sink_arg = req.mutable_sinks()->Add();
                if (sinks[0] == "StdoutSink")
                {
                    sink_arg->set_type(sink_type::StdoutSink);
                }
                else if (sinks[0] == "FileSink")
                {
                    sink_arg->set_type(sink_type::FileSink);
                }
                else if (sinks[0] == "RollSinkBySize")
                {
                    sink_arg->set_type(sink_type::RollSinkBySize);
                }
                else if (sinks[0] == "SqliteSink")
                {
                    sink_arg->set_type(sink_type::SqliteSink);
                }
                else if (sinks[0] == "MySQLSink")
                {
                    sink_arg->set_type(sink_type::MySQLSink);
                }
                for (int j = 1; j < sinks.size(); j++)
                {
                    sink_arg->mutable_args()->Add()->operator=(sinks[j]);
                }
            }
            if (!_conn->connected())
            {
                std::cout << "连接已断开, 无法声明日志器\n";
                return false;
            }
            // 2. 发送请求
            _codec.send(_conn, req);
            // 3. 等待响应
            commonResponsePtr resp = waitResponse(req.rid());
            std::cout << "声明日志器情况: " << resp->err_msg() << "\n";
            return resp->ok();
        }

        bool web_log(const std::vector<LogMessage::ptr> &message)
        {
            if (message.empty())
                return true;
            sinklogRequest req;
            req.set_rid(UuidHelper::uuid());
            req.set_logger_name(message[0]->_logger_name);
            std::ostringstream oss;
            for (int i = 0; i < message.size(); i++)
            {
                auto elem = message[i];
                logMessage *message_ptr = req.mutable_message()->Add();
                oss << elem->_thread_id;
                message_ptr->set_thread_id(oss.str());
                oss.clear();
                oss.str("");
                message_ptr->set_logger_name(elem->_logger_name);
                message_ptr->set_time(elem->_time);
                message_ptr->set_level(get_logLevel(elem->_level));
                message_ptr->set_file(elem->_file);
                message_ptr->set_line(elem->_line);
                message_ptr->set_body(elem->_body);
                // std::cout << "LogMessage的具体大小："<<elem->sz()<<"\tmessage [" << i << "] 的长度：" << message_ptr->ByteSizeLong() << "\t";
            }
            std::cout << "sinklogRequest的长度: " << req.ByteSizeLong() << "\n";
            std::cout << "message的个数: " << req.message_size() << "\n";
            if (!_conn->connected())
            {
                std::cout << "连接已断开, 无法发送日志器打印请求\n";
                return false;
            }
            _codec.send(_conn, req);
            commonResponsePtr resp = waitResponse(req.rid());
            // std::cout << "打印日志情况: " << resp->err_msg() << "\n";
            return resp->ok();
        }

    private: // 业务处理
        loggerType getloggerType(LoggerType type)
        {
            return (type == LoggerType::SYNC) ? loggerType::SYNC : loggerType::ASYNC;
        }

        logLevel get_logLevel(LogLevel::value level)
        {
            switch (level)
            {
            case LogLevel::value::UNKNOWN:
                return logLevel::UNKNOWN;
            case LogLevel::value::DEBUG:
                return logLevel::DEBUG;
            case LogLevel::value::INFO:
                return logLevel::INFO;
            case LogLevel::value::WARNING:
                return logLevel::WARNING;
            case LogLevel::value::ERROR:
                return logLevel::ERROR;
            case LogLevel::value::FATAL:
                return logLevel::FATAL;
            case LogLevel::value::OFF:
                return logLevel::OFF;
            default:
                return logLevel::UNKNOWN;
            }
        }

        asyncType get_asyncType(AsyncType type)
        {
            return (type == AsyncType::ASYNC_SAFE) ? asyncType::ASYNC_SAFE : asyncType::ASYNC_UNSAFE;
        }

    private:
        void start()
        {
            _client.connect();
            _latch.wait();
        }

        void OnConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                _conn = conn;
                std::cout << "连接建立成功\n";
                _latch.countDown();
            }
            else
            {
                if (_conn.get() && _conn->connected())
                    _conn->shutdown();
                _conn.reset();
                std::cout << "连接断开成功\n";
            }
        }

        void OnUnknownCallback(const muduo::net::TcpConnectionPtr &conn, const MessagePtr &message, muduo::Timestamp)
        {
            std::cout << "未知请求, 即将断开连接\n";
            if (conn->connected())
                conn->shutdown();
        }

        void OnCommonResponse(const muduo::net::TcpConnectionPtr &conn, const commonResponsePtr &resp, muduo::Timestamp)
        {
            std::unique_lock<std::mutex> ulock(_mutex);
            _resp_map.insert(std::make_pair(resp->rid(), resp));
            _cv.notify_all();
        }

        commonResponsePtr waitResponse(const std::string &rid)
        {
            std::unique_lock<std::mutex> ulock(_mutex);
            _cv.wait(ulock, [this, &rid]() -> bool
                     { return _resp_map.count(rid); });
            return _resp_map[rid];
        }

        // 先初始化EventLoopThread, 后初始化TcpClient
        muduo::net::EventLoopThread _loop_thread;
        muduo::net::TcpClient _client;
        muduo::net::TcpConnectionPtr _conn;
        muduo::CountDownLatch _latch;

        ProtobufDispatcher _dispatcher;
        ProtobufCodec _codec;

        // 用来协调保持任务顺序性的哈希表
        std::mutex _mutex;
        std::condition_variable _cv;
        std::unordered_map<std::string, commonResponsePtr> _resp_map;
    };
}
